package main

import (
	"flag"
	"fmt"
	"time"
)

func longwait() {
	fmt.Println("Begining longWait()")
	time.Sleep(5*1e9)
	fmt.Println("End of longWit()")
}

func shortWait() {
	fmt.Println("Begining shortWait()")
	time.Sleep(2*1e9)
	fmt.Println("End of shortWait()")
}

func sendData(ch chan string) {
	ch <- "Washington"
	ch <- "Tripoli"
	ch <- "London"
	ch <- "Beijing"
	ch <- "Tokyo"
}

func getData(ch chan string) {
	var input string
	for {
		input =  <- ch
		fmt.Printf("%s ", input)
	}
}

func channelTest() {
	ch := make(chan string)
	go sendData(ch)
	go getData(ch)
	/**
	如果主协程不等待两个协程完成，sendData就没有机会输出。
	 */
	time.Sleep(1e9)
}

func pump(ch chan int) {
	for i := 0; ; i++ {
		ch <- i
	}
}

func blockTest() {
	ch := make(chan int)
	go pump(ch)
	fmt.Println(<-ch)
}

func f1(in chan int) {
	fmt.Println(<-in)
}

func blocking() {
	out := make(chan int)
	out <- 2
	go f1(out)
}

type Empty interface {

}

type semaphore chan Empty

func (s semaphore) P(n int) {
	e := new(Empty)
	for i := 0; i < n; i++ {
		s <- e
	}
}

func (s semaphore) V(n int) {
	for i := 0; i < n; i++ {
		 <- s
	}
}

func (s semaphore) Lock() {
	s.P(1)
}

func (s semaphore) Unlock() {
	s.V(1)
}

func (s semaphore) Wait(n int) {
	s.P(n)
}
func (s semaphore) Signal() {
	s.V(1)
}

func producer(in chan int, s *semaphore) {
	for i := 0; i < 100; i+=10 {
		in <- i
		s.Signal()
	}
}

func consumer(ch chan int, s *semaphore)  {
	for  {
		s.Wait(1)
		value := <- ch
		fmt.Println(value)
	}

}

func numGen(start, count int, out chan<- int) {
	for i := 0; i < count; i++ {
		out <- start
		start += count
	}
	close(out)
}

func numEcho(in <-chan int, done chan<- bool) {
	for num:= range in{
		fmt.Printf("%d\n", num)
	}
	done <- true
}

func test3() {
	numChan := make(chan int)
	done := make(chan bool)

	go numGen(0, 10, numChan)
	go numEcho(numChan, done)

	<-done
}

func test2() {
	ch := make(chan int)
	sem := new(semaphore)

	go consumer(ch, sem)
	go producer(ch, sem)


	time.Sleep(10*1e9)
}

func pump1() chan int {
	ch := make(chan int)
	go func() {
		for i := 0; ; i++{
			ch <- i
		}
	}()

	return ch
}

func suck1(ch chan int) {
	for true {
		fmt.Println(<-ch)
	}
}

func test4() {
	stream := pump1()
	go suck1(stream)

	time.Sleep(1e9)
}

func generate(ch chan int) {
	for i:=2;;i++{
		ch <- i
	}
}

func filter(in, out chan int, prime int) {
	for {
		i := <- in
		if i%prime != 0{
			out <- i
		}
	}
}

func test5() {
	ch := make(chan int)
	go generate(ch)
	for {
		prime := <- ch
		fmt.Print(prime, " ")
		ch1 := make(chan int)
		go filter(ch, ch1, prime)
		ch = ch1
	}
}

func generate2() chan int {
	ch := make(chan int)
	go func() {
		for i:=2; ; i++{
			ch <- i
		}
	}()
	return ch
}

func filter2(in chan int, prime int)chan int {
	out := make(chan int)
	go func() {
		for {
			if i:= <-in; i%prime != 0{
				out <- i
			}
		}
	}()
	return out
}

func sieve() chan int {
	out := make(chan int)
	go func() {
		ch := generate2()
		for {
			prime := <- ch
			ch = filter2(ch, prime)
			out <- prime
		}
	}()
	return out
}

func test6() {
	primes := sieve()
	for {
		fmt.Println(<-primes)
	}
}

func pump2(ch chan int) {
	for i:=0;;i++{
		ch <- i * 2
	}
}

func pump3(ch chan int) {
	for i := 0; ; i++ {
		ch <- i+5
	}
}

func suck(ch1, ch2 chan int) {
	for {
		select {
		case v:= <- ch1:
			fmt.Printf("Received on channel 1: %d\n", v)
		case v:=<- ch2:
			fmt.Printf("Received on channel 2: %d\n", v)

		}
	}
}

func test7() {
	ch1 := make(chan int)
	ch2 := make(chan int)

	go pump2(ch1)
	go pump3(ch2)
	go suck(ch1, ch2)

	time.Sleep(1e9)
}

func test8() {
	tick := time.Tick(1e8)
	boom := time.After(5e8)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("Boom!!")
			return
		default:
			fmt.Println("	.")
			time.Sleep(5e7)

		}
	}
}

var resume chan int

func integers() chan int {
	yield := make(chan int)
	count := 0
	go func() {
		for {
			yield <- count
			count++
		}
	}()
	return yield
}

func generateInteger() int {
	return <- resume
}

func test9() {
	resume = integers()
	fmt.Println(generateInteger())
	fmt.Println(generateInteger())
	fmt.Println(generateInteger())
}

type Any interface{}
type EvalFunc func(Any)(Any, Any)

func BuildLazyEvaluator(evalFunc EvalFunc, initState Any)func() Any  {
	retValChan := make(chan Any)
	loopFunc := func() {
		var actState Any = initState
		var retVal Any
		for {
			retVal, actState = evalFunc(actState)
			retValChan <- retVal
		}
	}
	retFunc := func() Any{
		return <- retValChan
	}

	go loopFunc()
	return retFunc
}

func BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {
	ef := BuildLazyEvaluator(evalFunc, initState)
	return func() int {
		return ef().(int)
	}
}

func test10() {
	evenFunc := func(state Any)(Any, Any) {
		os := state.(int)
		ns := os + 2
		return os, ns
	}

	even := BuildLazyIntEvaluator(evenFunc, 0)

	for i := 0; i < 10; i++ {
		fmt.Printf("%vth even: %d\n", i, even())
	}
}

type Request struct {
	a, b 		int
	replyc		chan int
}

type binOp func(a, b int)int

func run(op binOp, req *Request)  {
	req.replyc <- op(req.a, req.b)
}

func startServer(op binOp) chan *Request {
	reqChan := make(chan *Request)
	go server(op, reqChan)
	return reqChan
}
func server(op binOp, server chan *Request) {
	for {
		req := <-server
		go run(op, req)
	}
}

func test11() {
	adder := startServer(func(a, b int) int {
		return a + b
	})
	const N = 100
	var reqs [N]Request
	for i := 0; i < N; i++ {
		req := &reqs[i]
		req.a = i
		req.b = i + N
		req.replyc = make(chan int)
		adder <- req
	}

	for i:=N-1;i>=0;i--{
		if <-reqs[i].replyc != N + 2*i{
			fmt.Println("Fail at ", i)
		}else {
			fmt.Println("Requst ", i, " is ok!")
		}
	}

	fmt.Println("Down!")
}

var ngoroutine = flag.Int("n", 100000, "how many goroutines")

func f(left, right chan int) {
	left <- 1 + <-right
}

func test12() {
	flag.Parse()
	leftmost := make(chan int)
	var left, right chan int = nil, leftmost
	for i := 0; i < *ngoroutine; i++ {
		left, right = right, make(chan int)
		go f(left, right)
	}
	right <- 0
	x := <- leftmost
	fmt.Println(x)
}

func main() {
	//test()
	//channelTest()
	//blockTest()
	//blocking()
	//test2()
	//test3()
	//test4()
	//test5()
	//test6()
	//test7()
	//test8()
	//test9()
	//test10()
	//test11()
	test12()

}

func test() {
	fmt.Println("In main()")
	go longwait()
	go shortWait()
	fmt.Println("About to sleep in main()")
	time.Sleep(10 * 1e9)
	fmt.Println("At the end of main()")
}
